package main

import (
	"fmt"
	"sync"
)

func main() {

	/* case 1 :该示例：main函数退出，所有的协程都会退出
	输出结果：
	goroutine 1: i=1
	goroutine 1: i=2
	goroutine 1: i=3
	main exit


		go func() {
		i := 0

		for {
			i++
			fmt.Printf("goroutine 1: i=%d\n", i)
			time.Sleep(time.Second)
		}
	}()

	time.Sleep(3 * time.Second)
	fmt.Println("main exit")

	*/

	/* case 2
	//协程无父子关系，即在一个协程开启新的协程，该协程退出，不影响新的协程
	go func() {
		go func() {
			i := 0
			for {
				fmt.Printf("goroutine 2 : i=%d\n", i)
				time.Sleep(time.Second)
				i++
			}
		}()

		j := 0
		for {
			j++
			fmt.Printf("goroutine 1 : i=%d\n", j)
			time.Sleep(500 * time.Millisecond)
			if j == 3 {
				break
			}
		}
		fmt.Println("goroutine 1 exit")
	}()

	// 永远堵塞main
	select {}
	fmt.Println("main exit")

	*/

	/*
		//当我们的上一级goroutine停止时, 我们希望它下级的所有goroutine也能收到这个通知及时停止, 防止资源浪费.
		go func() {
			ctx, cancel := context.WithCancel(context.Background())
			defer cancel()

			go func(ctx context.Context) {
				i := 0
				for {
					select {
					case <-ctx.Done():
						fmt.Printf("goroutine 2 exit\n")
						return
					default:
						fmt.Printf("goroutine 2: i = %d\n", i)
						time.Sleep(time.Second)
						i++
					}
				}
			}(ctx)

			for j := 0; j <= 3; j++ {
				fmt.Printf("goroutine 1: i = %d\n", j)
				time.Sleep(500 * time.Millisecond)
			}

			fmt.Println("goroutine 1 exit")
		}()

		// 永远堵塞main
		select {}
		fmt.Println("Main exit")

	*/

	// 封装好的计数器
	var counter Counter
	var wg sync.WaitGroup
	wg.Add(10)

	// 启动10个goroutine
	for i := 0; i < 10; i++ {
		go func() {
			defer wg.Done()
			// 执行10万次累加
			for j := 0; j < 100000; j++ {
				counter.Incr() // 受到锁保护的方法
			}
		}()
	}
	wg.Wait()
	fmt.Println(counter.Count())
}

//Counter 线程安全的计数器类型
type Counter struct {
	CounterType int
	Name        string
	mu          sync.Mutex
	count       uint64
}

//Incr 加1的方法，内部使用互斥锁保护
func (c *Counter) Incr() {
	c.mu.Lock()
	c.count++
	c.mu.Unlock()
}

//Count 得到计数器的值，也需要锁保护
func (c *Counter) Count() uint64 {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.count
}
